//
//  commo.c
//  myvpn
//
//  Created by Zhuobin He on 12-12-31.
//  Copyright (c) 2012年 Zhuobin He. All rights reserved.
//

#include "SysLib.h"

#include "Common.h"

PConfig CONFIG;

bool check_str(const char *str) {
    return str && strlen(str);
}


char * trim(char *str) {
    {
        char *i1 = str + strspn(str, WHITESPACE);
        if (i1 != str) {
            memmove(str, i1, sizeof(char) * (strlen(i1) + 1));
        }
    }
    int i;
    for (i=(int)strlen(str)-1; i>=0; i--) {
        if (strchr(WHITESPACE, str[i]) == NULL) break;
        str[i] = '\0';
    }
    return str;
}

void zmalloc(void *dst, size_t size) {
    void **tmp = (void **)dst;
    (*tmp) = malloc(size);
    bzero(*tmp, size);
}



int execute_cmd(const char *cmd, char *result, int limit) {
    FILE *ptr;
    char buffer[1030];
    if((ptr=popen(cmd, "r"))!=NULL) {
        while(fgets(buffer, 1024, ptr)!=NULL) {
            int nread = (int)strlen(buffer);
            if (limit > 1) {
                nread = min(nread, limit - 1);
                strncpy(result, buffer, nread);
                result[nread] = '\0';
                result += nread;
                limit -= nread;
            }
        }
        pclose(ptr);
        ptr = NULL;
        return 0;
    }
    else {
        log_error("popen %s error\n", cmd);
        return 1;
    }
}


int _log_level = LOG_INFO;

FILE *log_fd = NULL;


bool is_log_debug() {
    return LOG_DEBUG <= _log_level;
}

void _do_logging(int level, const char *msg, va_list argp) {
    if (level <= _log_level) {
        time_t now = time(NULL);
        char time_str[25];
        strftime(time_str, 24, "%Y-%m-%d %H:%M:%S", localtime(&now));
        char log_content[1024];
        if (strlen(msg) > 800) {
            strncpy(log_content, msg, 800);
            log_content[800] = log_content[801] = log_content[802] = '.';
            log_content[803] = '\0';
        } else {
            vsprintf(log_content, msg, argp);
        }
        switch (level) {
            case LOG_ERROR:
                fprintf(log_fd, "%s %s %s\n", time_str, "[ERROR]", log_content);
                break;
            case LOG_INFO:
                fprintf(log_fd, "%s %s %s\n", time_str, "[INFO]", log_content);
                break;
            case LOG_DEBUG:
                fprintf(log_fd, "%s %s %s\n", time_str, "[DEBUG]", log_content);
                break;
            case LOG_TRACE:
                fprintf(log_fd, "%s %s %s\n", time_str, "[TRACE]", log_content);
                break;
        }
        fflush(log_fd);
    }
}

void log_error(const char *msg, ...) {
    va_list argp;
    va_start(argp, msg);
    _do_logging(LOG_ERROR, msg, argp);
    va_end(argp);
}

void log_info(const char *msg, ...) {
    va_list argp;
    va_start(argp, msg);
    _do_logging(LOG_INFO, msg, argp);
    va_end(argp);
}


#ifdef LOG_DEBUG_ENABLE
void log_debug(const char *msg, ...) {
    va_list argp;
    va_start(argp, msg);
    _do_logging(LOG_DEBUG, msg, argp);
    va_end(argp);
}

void log_trace(const char *msg, ...) {
    va_list argp;
    va_start(argp, msg);
    _do_logging(LOG_TRACE, msg, argp);
    va_end(argp);
}
#endif


char * string_alloc(const char *str) {
    char *ret = (char *) malloc (sizeof(char) * (strlen(str) + 1));
    strcpy(ret, str);
    return ret;
}

int str2int(const char *str) {
    int i;
    sscanf(str, "%d", &i);
    return i;
}


void ip_str_to_num(char *str, int limit, unsigned char *dst) {
    int i;
    char *head = str = string_alloc(str);
    for (i=0; i<limit; i++) {
        char *tmp = strchr(str, '.');
        if (tmp) {
            tmp[0] = '\0';
        }
        dst[i] = (unsigned char)str2int(str);
        if (tmp == NULL) break;
        str = tmp + 1;
    }
    free(head);
}


struct sockaddr_in * sockaddr_by_ip_port(const char *host, int port) {
    struct sockaddr_in *ret = NULL;
    zmalloc(&ret, sizeof(struct sockaddr_in));
    ret->sin_family = AF_INET;

    struct hostent *h = gethostbyname(host);
    ret->sin_addr.s_addr = *(unsigned long * ) h->h_addr;
    ret->sin_port = htons(port);
    return ret;
}

PClientInfo new_client_info(struct sockaddr_in *src_addr, int sock_src, struct sockaddr_in *remote_addr) {
    PClientInfo ret = NULL;
    zmalloc(&ret, sizeof(ClientInfo));
    ret->src_ip = src_addr->sin_addr.s_addr;
    ret->src_port = src_addr->sin_port;
    ret->sock_src = sock_src;
    ret->remote_ip = remote_addr->sin_addr.s_addr;
    ret->remote_port = remote_addr->sin_port;
    ret->remote_port2 = 0;
    ret->sock_write = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
    ret->last_send_time = ret->last_recv_time = time(0);
    return ret;
}

PConfig load_config(int argc, char **argv) {
    char *short_opts = "hHPStrnmusl:";
    struct option long_options[] = {
        {"help" ,no_argument , NULL , 'h'},
        {"host" ,required_argument , NULL , 'H'},
        {"port" , required_argument, NULL , 'P'},
        {"server" , required_argument , NULL , 'S'},
        {"timeout" , required_argument , NULL , 't'},
        {"report_interval" , required_argument , NULL , 'r'},
        {"renew_interval" , required_argument , NULL , 'n'},
        {"min_packet" , required_argument , NULL , 'm'},
        {"user" , required_argument , NULL , 'u'},
        {"sync_url" , required_argument , NULL , 's'},
        {"log_level" , required_argument , NULL , 'l'},
        {NULL , 0 , NULL , 0}
    };

    PConfig ret = NULL;
    zmalloc(&ret, sizeof(Config));
    ret->mode = CLIENT;
    ret->log_level = LOG_TRACE;
    ret->log_file = NULL;

    ret->bind_ip = "127.0.0.1";
    ret->bind_port = 1194;

    ret->server_ip = "116.251.211.73";

    ret->timeout = 60;
    ret->report_interval = 60;
    ret->renew_interval = 3;

    ret->min_packet = 320;

    ret->user = "anonymous";
    ret->sync_url = "/sync.data";
    int opt;
    while((opt = getopt_long(argc , argv, short_opts,  long_options, NULL)) != -1)
    {
        switch(opt) {
        case 'H':
            ret->bind_ip = string_alloc(optarg);
            break;
        case 'P':
            ret->bind_port = str2int(optarg);
            break;
        case 'S':
            ret->server_ip = string_alloc(optarg);
            break;
        case 't':
            ret->timeout = str2int(optarg);
            break;
        case 'r':
            ret->report_interval = str2int(optarg);
            break;
        case 'n':
            ret->renew_interval = str2int(optarg);
            break;
        case 'm':
            ret->min_packet = str2int(optarg);
            break;
        case 'u':
            ret->user = string_alloc(optarg);
            break;
        case 's':
            ret->sync_url = string_alloc(optarg);
            break;
        case 'l':
            ret->log_level = str2int(optarg);
            break;
        case 'h':
            printf("--help, -h\n\tPrint this help\n--host, -H <ip>\n\tBind to address\n--port, -P <port>\n\tBind to port\n--server, -S <ip>\n\tProxy server address\n--timeout, -t <second>\n\tConnection timeout in second\n--report_interval, -r <second>\n\tReport data lost stats every targeted second\n--renew_interval, -n <second>\n\tRenew data port after every targeted second\n--min_packet, -m <bytes>\n\tMin packet size in bytes\n--user, -u <username>\n\tUser name\n--sync_url, -s <url>\n\tSync url\n--log_level, -l <level>\n\tLog level");
            exit(0);
            break;
        }
    }

    _log_level = ret->log_level;
    if (ret->log_file == NULL) {
        log_fd = stdout;
    } else {
        log_fd = fopen(ret->log_file, "a");
    }
    CONFIG = ret;

    return ret;
}
